#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kmod.h>     // For call_usermodehelper
#include <linux/slab.h>     // For kmalloc/kfree
#include <linux/err.h>      // For IS_ERR/PTR_ERR
// 不需要 <linux/wait.h>，因为它的 WIFEXITED 等宏可能不是用于解析 call_usermodehelper 返回值的。

// 用户空间程序退出状态的解析宏（等效于 sys/wait.h 中的宏）
// Linux kernel 内部处理 wait status 的方式，与用户空间的 waitpid 是一致的。
// 低8位用于信号，高8位用于退出码。
#define KERNEL_WIFEXITED(status)    (((status) & 0x7f) == 0)
#define KERNEL_WEXITSTATUS(status)  (((status) >> 8) & 0xff)
#define KERNEL_WIFSIGNALED(status)  (((status) & 0x7f) > 0 && ((status) & 0x7f) != 0x7f)
#define KERNEL_WTERMSIG(status)     ((status) & 0x7f)

// 要执行的用户空间程序的路径
#define USERSPACE_APP_PATH "/home/linux/workspace/jiami/my_helper_script.sh"

static int my_execute_userspace_app(const char *arg1, const char *arg2) {
    char *argv[4]; // Program path, arg1, arg2, NULL terminator
    char *envp[2]; // PATH environment variable, NULL terminator
    int wait_status;        // call_usermodehelper 的原始返回值
    int actual_exit_code = -1; // 用于存储实际的用户程序退出码

    // 分配并设置 argv 数组
    argv[0] = kstrdup(USERSPACE_APP_PATH, GFP_KERNEL);
    if (!argv[0]) {
        pr_err("Failed to allocate argv[0]\n");
        return -ENOMEM;
    }
    argv[1] = kstrdup(arg1, GFP_KERNEL);
    if (!argv[1]) {
        pr_err("Failed to allocate argv[1]\n");
        kfree(argv[0]);
        return -ENOMEM;
    }
    argv[2] = kstrdup(arg2, GFP_KERNEL);
    if (!argv[2]) {
        pr_err("Failed to allocate argv[2]\n");
        kfree(argv[0]);
        kfree(argv[1]);
        return -ENOMEM;
    }
    argv[3] = NULL; // 必须以 NULL 结尾

    // 分配并设置 envp 数组
    envp[0] = kstrdup("PATH=/sbin:/bin:/usr/sbin:/usr/bin", GFP_KERNEL);
    if (!envp[0]) {
        pr_err("Failed to allocate envp[0]\n");
        kfree(argv[0]);
        kfree(argv[1]);
        kfree(argv[2]);
        return -ENOMEM;
    }
    envp[1] = NULL; // 必须以 NULL 结尾

    pr_info("Calling userspace helper: %s %s %s\n", argv[0], argv[1], argv[2]);

    // 调用 call_usermodehelper，并等待其完成以获取原始的 wait status
    wait_status = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);

    // 释放 kmalloc 分配的字符串
    kfree(argv[0]);
    kfree(argv[1]);
    kfree(argv[2]);
    kfree(envp[0]);

    if (wait_status < 0) {
        // call_usermodehelper 本身失败，例如路径错误，内存不足等
        pr_err("call_usermodehelper returned error: %d\n", wait_status);
        return wait_status;
    }

    // 解析 wait_status 以获取实际的用户程序退出码
    if (KERNEL_WIFEXITED(wait_status)) {
        actual_exit_code = KERNEL_WEXITSTATUS(wait_status);
        pr_info("Userspace helper exited normally with code: %d (raw status: %d)\n", actual_exit_code, wait_status);
    } else if (KERNEL_WIFSIGNALED(wait_status)) {
        // 如果程序被信号终止
        actual_exit_code = -KERNEL_WTERMSIG(wait_status); // 返回负的信号值，方便区分
        pr_warn("Userspace helper terminated by signal: %d (raw status: %d)\n", KERNEL_WTERMSIG(wait_status), wait_status);
    } else {
        // 其他情况（例如被停止，但 UMH_WAIT_PROC 不太可能出现这种状态）
        pr_warn("Userspace helper exited with unknown status: %d (raw status: %d)\n", wait_status, wait_status);
        actual_exit_code = -1; // 表示未知或非正常退出
    }

    // 返回实际的退出码
    return actual_exit_code;
}


static int __init my_module_init(void) {
    int ret;
    pr_info("My module loaded, attempting to execute userspace app...\n");

    // 第一次执行：模拟成功
    ret = my_execute_userspace_app("success", "data_a");
    pr_info("First execution finished, driver got return: %d\n", ret);

    // 第二次执行：模拟失败
    ret = my_execute_userspace_app("fail", "data_b");
    pr_info("Second execution finished, driver got return: %d\n", ret);

    // 第三次执行：模拟未知状态
    ret = my_execute_userspace_app("unknown", "data_c");
    pr_info("Third execution finished, driver got return: %d\n", ret);


    pr_info("Module init complete.\n");
    return 0;
}

static void __exit my_module_exit(void) {
    pr_info("My module unloaded.\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Example of executing userspace app from kernel module");
MODULE_VERSION("0.1");
